home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Peter Lewis / PNL Libraries / MyWindows.p < prev    next >
Encoding:
Text File  |  1994-08-29  |  13.6 KB  |  372 lines  |  [TEXT/PJMM]

  1. unit MyWindows;
  2.  
  3. { Based heavilly on Dean Yu's Develop #17 code }
  4.  
  5. interface
  6.  
  7. {$IFC undefined THINK_Pascal}
  8.     uses
  9.         Windows, QuickDraw;
  10. {$ENDC}
  11.  
  12.     procedure ZoomTheWindow (theWindow: WindowPtr; zoomout: boolean; idealsize: point; var unzoomed: rect);
  13.     procedure ZoomWindowOut (theWindow: WindowPtr; idealsize: point);
  14.     procedure GetWindowRect (theWindow: WindowPtr; var r: rect);
  15.     procedure SetWindowRect (theWindow: WindowPtr; var r: rect);
  16.     function GetWindowContentRegion (theWindow: WindowPtr): RgnHandle;
  17.     function GetWindowStructureRegion (theWindow: WindowPtr): RgnHandle;
  18.     procedure GetWindowPortRect (theWindow: WindowPtr; var portRect: rect);
  19.     function GetWindowVisible (theWindow: WindowPtr): boolean;
  20.     procedure GetWindowStandardState (theWindow: WindowPtr; var standardState: Rect);
  21.     procedure SetWindowStandardState (theWindow: WindowPtr; standardState: Rect);
  22.     procedure GetWindowUserState (theWindow: WindowPtr; var userState: Rect);
  23.     procedure SetWindowUserState (theWindow: WindowPtr; userState: Rect);
  24.     function TitleBarOnScreen (wp: WindowPtr): boolean;
  25.  
  26. implementation
  27.  
  28. { Based on code by Dean Yu in Develop 17 }
  29. { Changes: }
  30. { Converted to Pascal }
  31. { Pass in desired window size instead of a ProcPtr to return the desired window size }
  32. { Removed use of DeviceLoop }
  33. {   (DeviceLoop is System 7 dependent, and doesn't work in THINK Pascal anyway due to a bug in the interfaces }
  34. { Improved to handle zooming windows before they are made visible (since struct and content rgn's are empty) }
  35.  
  36.     uses
  37.         Script, MySystemGlobals;
  38.  
  39.     const
  40.         kNudgeSlop = 4;
  41.         kIconSpace = 64;
  42.  
  43. { WindowRecord accessor functions }
  44.  
  45.     function GetWindowContentRegion (theWindow: WindowPtr): RgnHandle;
  46.     begin
  47.         GetWindowContentRegion := WindowPeek(theWindow)^.contRgn;
  48.     end;
  49.  
  50.     function GetWindowStructureRegion (theWindow: WindowPtr): RgnHandle;
  51.     begin
  52.         GetWindowStructureRegion := WindowPeek(theWindow)^.strucRgn;
  53.     end;
  54.  
  55.     procedure GetWindowPortRect (theWindow: WindowPtr; var portRect: rect);
  56.     begin
  57.         portRect := WindowPeek(theWindow)^.port.portRect;
  58.     end;
  59.  
  60.     function GetWindowVisible (theWindow: WindowPtr): boolean;
  61.     begin
  62.         GetWindowVisible := WindowPeek(theWindow)^.visible;
  63.     end;
  64.  
  65.     procedure GetWindowStandardState (theWindow: WindowPtr; var standardState: Rect);
  66.     begin
  67.         standardState := WStateDataHandle(WindowPeek(theWindow)^.dataHandle)^^.stdState;
  68.     end;
  69.  
  70.     procedure SetWindowStandardState (theWindow: WindowPtr; standardState: Rect);
  71.     begin
  72.         WStateDataHandle(WindowPeek(theWindow)^.dataHandle)^^.stdState := standardState;
  73.     end;
  74.  
  75.     procedure GetWindowUserState (theWindow: WindowPtr; var userState: Rect);
  76.     begin
  77.         userState := WStateDataHandle(WindowPeek(theWindow)^.dataHandle)^^.userState;
  78.     end;
  79.  
  80.     procedure SetWindowUserState (theWindow: WindowPtr; userState: Rect);
  81.     begin
  82.         WStateDataHandle(WindowPeek(theWindow)^.dataHandle)^^.userState := userState;
  83.     end;
  84.  
  85.     procedure GetWindowRect (theWindow: WindowPtr; var r: rect);
  86.     begin
  87.         SetPort(theWindow);
  88.         GetWindowPortRect(theWindow, r);
  89.         LocalToGlobal(r.topleft);
  90.         LocalToGlobal(r.botright);
  91.     end;
  92.  
  93.     procedure SetWindowRect (theWindow: WindowPtr; var r: rect);
  94.         var
  95.             scratchRegion: rgnHandle;
  96.             portRect: rect;
  97.     begin
  98.         GetWindowPortRect(theWindow, portRect);
  99.         scratchRegion := NewRgn;
  100.         GetClip(scratchRegion);
  101.         ClipRect(portRect);
  102.         EraseRect(portRect);
  103.         SetWindowStandardState(theWindow, r);
  104.         ZoomWindow(theWindow, inZoomOut, false);
  105.         SetClip(scratchRegion);
  106.         DisposeRgn(scratchRegion);
  107.     end;
  108.  
  109.     function GetBestDevice (windowBounds: rect): GDHandle;
  110.         var
  111.             thisGD, bestGD: GDHandle;
  112.             thisArea, bestArea: longInt;
  113.             thisBounds: rect;
  114.             dummy: boolean;
  115.     begin
  116.         thisGD := GetDeviceList;
  117.         bestArea := 0;
  118.         bestGD := GetMainDevice;
  119.         while thisGD <> nil do begin
  120.             if TestDeviceAttribute(thisGD, screenDevice) & TestDeviceAttribute(thisGD, screenActive) then begin
  121.                 dummy := SectRect(windowBounds, thisGD^^.gdRect, thisBounds);
  122.                 thisArea := longInt(thisBounds.right - thisBounds.left) * longInt(thisBounds.bottom - thisBounds.top);
  123.                 if thisArea > bestArea then begin
  124.                     bestGD := thisGD;
  125.                     bestArea := thisArea;
  126.                 end;
  127.             end;
  128.             thisGD := GetNextDevice(thisGD);
  129.         end;
  130.         GetBestDevice := bestGD;
  131.     end;
  132.  
  133. { Figure out how much we need to move the window to get it entirely on the monitor.  If }
  134. { the window wouldn’t fit completely on the monitor anyway, don’t move it at all; we’ll }
  135. { make it fit later on. }
  136.  
  137.     function CalculateOffsetAmount (idealStartPoint, idealEndPoint, idealOnScreenStartPoint, idealOnScreenEndPoint, screenEdge1, screenEdge2: integer): integer;
  138.         var
  139.             offsetAmount: integer;
  140.     begin
  141.     { First check to see if the window fits on the screen in this dimension. }
  142.         if (idealStartPoint < screenEdge1) & (idealEndPoint > screenEdge2) then begin
  143.             offsetAmount := 0;
  144.         end
  145.         else begin
  146.  
  147.         { Find out how much of the window lies off this screen by subtracting the amount of the window }
  148.         { that is on the screen from the size of the entire window in this dimension. If the window }
  149.         { is completely offscreen, the offset amount is going to be the distance from the ideal }
  150.         { starting point to the first edge of the screen. }
  151.             if idealOnScreenStartPoint - idealOnScreenEndPoint = 0 then begin
  152.             { See if the window is lying to the left or above the screen }
  153.                 if idealEndPoint < screenEdge1 then begin
  154.                     offsetAmount := screenEdge1 - idealStartPoint + kNudgeSlop;
  155.                 end
  156.                 else begin
  157.             { Otherwise, it’s below or to the right of the screen }
  158.                     offsetAmount := screenEdge2 - idealEndPoint - kNudgeSlop;
  159.                 end;
  160.             end
  161.             else begin
  162.             { Window is already partially or completely on the screen }
  163.                 offsetAmount := (idealEndPoint - idealStartPoint) - (idealOnScreenEndPoint - idealOnScreenStartPoint);
  164.  
  165.             { If we are offscreen a little, move the window in a few more pixels from the edge of the screen. }
  166.                 if offsetAmount <> 0 then begin
  167.                     offsetAmount := offsetAmount + kNudgeSlop;
  168.                 end;
  169.  
  170.             { Check to see which side of the screen the window was falling off of, so that it can be }
  171.             { nudged in the opposite direction. }
  172.                 if idealEndPoint > screenEdge2 then begin
  173.                     offsetAmount := -offsetAmount;
  174.                 end;
  175.             end;
  176.         end;
  177.  
  178.         CalculateOffsetAmount := offsetAmount;
  179.     end;
  180.  
  181.     procedure AddRect (r1, r2: rect; var r: rect);
  182.     begin
  183.         r.top := r1.top + r2.top;
  184.         r.bottom := r1.bottom + r2.bottom;
  185.         r.left := r1.left + r2.left;
  186.         r.right := r1.right + r2.right;
  187.     end;
  188.  
  189.     procedure SubRect (r1, r2: rect; var r: rect);
  190.     begin
  191.         r.top := r1.top - r2.top;
  192.         r.bottom := r1.bottom - r2.bottom;
  193.         r.left := r1.left - r2.left;
  194.         r.right := r1.right - r2.right;
  195.     end;
  196.  
  197.     procedure ZoomWindowOut (theWindow: WindowPtr; idealsize: point);
  198.         var
  199.             screenWithLargestPartOfWindow: GDHandle;
  200.             windowBounds: rect;
  201.             newStandardRect: rect;
  202.             scratchRect: rect;
  203.             screenRect: rect;
  204.             portRect: rect;
  205.             contentRegionBoundingBox: rect;
  206.             structureRegionBoundingBox: rect;
  207.             deviceLoopRect: rect;
  208.             scratchRegion: RgnHandle;
  209.             structureRegion: RgnHandle;
  210.             contentRegion: RgnHandle;
  211.             on_main_device: boolean;
  212.             horizontalAmountOffScreen: integer;
  213.             verticalAmountOffScreen: integer;
  214.             windowFrame: rect;
  215.             dummy: boolean;
  216.             orgrect: rect;
  217.             zstate: integer;
  218.     begin
  219.         SetPort(theWindow);
  220.  
  221.         GetWindowRect(theWindow, orgrect);
  222.  
  223.         contentRegion := GetWindowContentRegion(theWindow);
  224.         structureRegion := GetWindowStructureRegion(theWindow);
  225.         GetWindowPortRect(theWindow, portRect);
  226.  
  227. { If the window is invisible (or at least initially before it is ever made visible), then the content and structure }
  228. { regions will be empty.  In this case, we fake it out by using the portRect as the content region and 18 (hardcoded) }
  229. { as the titlebar height }
  230.         if EmptyRgn(structureRegion) then begin
  231.             scratchRect := portRect;
  232.             LocalToGlobal(scratchRect.topleft);
  233.             LocalToGlobal(scratchRect.botright);
  234.             contentRegionBoundingBox := scratchRect;
  235.             scratchRect.top := scratchRect.top - 18; { No other way of figuring out the window frame }
  236.             structureRegionBoundingBox := scratchRect;
  237.         end
  238.         else begin
  239.             contentRegionBoundingBox := contentRegion^^.rgnBBox;
  240.             structureRegionBoundingBox := structureRegion^^.rgnBBox;
  241.         end;
  242.  
  243.     { Determine the size of the window frame }
  244.         windowFrame.top := structureRegionBoundingBox.top - contentRegionBoundingBox.top;
  245.         windowFrame.left := structureRegionBoundingBox.left - contentRegionBoundingBox.left;
  246.         windowFrame.right := structureRegionBoundingBox.right - contentRegionBoundingBox.right;
  247.         windowFrame.bottom := structureRegionBoundingBox.bottom - contentRegionBoundingBox.bottom;
  248.  
  249.     { If the window is being zoomed into the standard state, calculate the best size }
  250.     { to display the window’s information. }
  251.         { Usually, we would use the content region’s bounding box to determine the monitor }
  252.         { with largest portion of the window’s area. However, if the entire content region }
  253.         { of the window is not on any screen, the structure region should be used instead. }
  254.         windowBounds := contentRegionBoundingBox;
  255.         scratchRegion := NewRgn;
  256.         RectRgn(scratchRegion, windowBounds);
  257.         SectRgn(GetGrayRgn, scratchRegion, scratchRegion);
  258.         if EmptyRgn(scratchRegion) then begin
  259.             windowBounds := structureRegionBoundingBox;
  260.         end;
  261.         DisposeRgn(scratchRegion);
  262.  
  263.         if has_colorQD then begin
  264.             screenWithLargestPartOfWindow := GetBestDevice(windowBounds);
  265.             screenRect := screenWithLargestPartOfWindow^^.gdRect;
  266.             on_main_device := GetMainDevice = screenWithLargestPartOfWindow;
  267.         end
  268.         else begin
  269. {$IFC undefined THINK_Pascal}
  270.             screenRect := qd.screenBits.bounds;
  271. {$ELSEC}
  272.             screenRect := screenBits.bounds;
  273. {$ENDC}
  274.             on_main_device := true;
  275.         end;
  276.  
  277.     { If the monitor being zoomed to is the main monitor, change the top of the }
  278.         { useable screen area to avoid putting the title bar underneath the menubar. }
  279.         if on_main_device then begin
  280.             screenRect.top := screenRect.top + GetMBarHeight;
  281.         end;
  282.  
  283.         { Go figure out the perfect size for the window as if we had an infinitely large }
  284.         { screen }
  285.     {    (*calcRoutine)((WindowPtr) theWindow, &newStandardRect);}
  286.         SetRect(newStandardRect, 0, 0, idealsize.h, idealsize.v);
  287.  
  288.         { Anchor the new rectangle at the window’s current top left corner }
  289.         { OffsetRect(&newStandardRect, -newStandardRect.left, -newStandardRect.top); }
  290.         OffsetRect(newStandardRect, orgrect.left, orgrect.top);
  291.  
  292.         { newStandardRect is the ideal size for the content area. The window frame }
  293.         { needs to be accounted for when we see if the window needs to be moved, }
  294.         { or resized, so add in the dimensions of the window frame.}
  295.         AddRect(newStandardRect, windowFrame, newStandardRect);
  296.  
  297. {        { If the new rectangle falls off the edge of the screen, nudge it so that it’s just }
  298.         { on the screen. CalculateOffsetAmount determines how much of the window is offscreen. }
  299.         dummy := SectRect(newStandardRect, screenRect, scratchRect);
  300.         if not EqualRect(newStandardRect, scratchRect) then begin
  301.             horizontalAmountOffScreen := CalculateOffsetAmount(newStandardRect.left, newStandardRect.right, scratchRect.left, scratchRect.right, screenRect.left, screenRect.right);
  302.             verticalAmountOffScreen := CalculateOffsetAmount(newStandardRect.top, newStandardRect.bottom, scratchRect.top, scratchRect.bottom, screenRect.top, screenRect.bottom);
  303.             OffsetRect(newStandardRect, horizontalAmountOffScreen, verticalAmountOffScreen);
  304.         end;
  305.  
  306.         { If we’re still falling off the edge of the screen, that means that the perfect }
  307.         { size is larger than the screen, so we need to shrink down the standard size }
  308.         dummy := SectRect(newStandardRect, screenRect, scratchRect);
  309.         if not EqualRect(newStandardRect, scratchRect) then begin
  310.  
  311.         { First shrink the width of the window. If the window is wider than the screen }
  312.         { it is zooming to, we can just pin the standard rectangle to the edges of the }
  313.         { screen, leaving some slop. If the window is narrower than the screen, we know }
  314.         { we just nudged it into position, so nothing needs to be done. }
  315.             if newStandardRect.right - newStandardRect.left > screenRect.right - screenRect.left then begin
  316.                 newStandardRect.left := screenRect.left + kNudgeSlop;
  317.  
  318.                 if (on_main_device) then begin
  319.                     newStandardRect.right := screenRect.right - kIconSpace;
  320.                 end
  321.                 else begin
  322.                     newStandardRect.right := screenRect.right - kNudgeSlop;
  323.                 end;
  324.             end;
  325.  
  326.             { Move in the top. Like the width of the window, nothing needs to be done unless }
  327.             { the window is taller than the height of the screen. }
  328.             if newStandardRect.bottom - newStandardRect.top > screenRect.bottom - screenRect.top then begin
  329.                 newStandardRect.top := screenRect.top + kNudgeSlop;
  330.                 newStandardRect.bottom := screenRect.bottom - kNudgeSlop;
  331.             end;
  332.         end;
  333.  
  334.         { We’ve got the best possible window position. Remove the }
  335.         { frame, slam it into the WStateData record and let ZoomWindow }
  336.         { take care of the rest. }
  337.         SubRect(newStandardRect, windowFrame, newStandardRect);
  338.  
  339.         if (newStandardRect.left = orgrect.left) & (newStandardRect.top = orgrect.top) then begin
  340.             SizeWindow(theWindow, newStandardRect.right - newStandardRect.left, newStandardRect.bottom - newStandardRect.top, true);
  341.         end
  342.         else begin
  343.             SetWindowRect(theWindow, newStandardRect);
  344.         end;
  345. { If the window is still anchored at the current location, then just resize it }
  346.     end;
  347.  
  348.     procedure ZoomTheWindow (theWindow: WindowPtr; zoomout: boolean; idealsize: point; var unzoomed: rect);
  349.     begin
  350.         SetPort(theWindow);
  351.         if zoomout then begin
  352.             GetWindowRect(theWindow, unzoomed);
  353.             ZoomWindowOut(theWindow, idealsize);
  354.         end
  355.         else begin
  356.             SetWindowRect(theWindow, unzoomed);
  357.         end;
  358.     end;
  359.  
  360.     function TitleBarOnScreen (wp: WindowPtr): boolean;
  361.         var
  362.             rgn: RgnHandle;
  363.     begin
  364.         rgn := NewRgn;
  365.         CopyRgn(GetWindowStructureRegion(wp), rgn);
  366.         DiffRgn(rgn, GetWindowContentRegion(wp), rgn);
  367.         SectRgn(rgn, GetGrayRgn, rgn);
  368.         TitleBarOnScreen := not EmptyRgn(rgn);
  369.         DisposeRgn(rgn);
  370.     end;
  371.  
  372. end.